package com.linxuan.reggie.controller;


import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import com.linxuan.reggie.common.R;
import com.linxuan.reggie.dto.DishDto;
import com.linxuan.reggie.dto.SetmealDto;
import com.linxuan.reggie.entity.Category;
import com.linxuan.reggie.entity.Dish;
import com.linxuan.reggie.entity.Setmeal;
import com.linxuan.reggie.entity.SetmealDish;
import com.linxuan.reggie.service.CategoryService;
import com.linxuan.reggie.service.DishService;
import com.linxuan.reggie.service.SetmealDishService;
import com.linxuan.reggie.service.SetmealService;
import io.swagger.annotations.Api;
import io.swagger.annotations.ApiImplicitParam;
import io.swagger.annotations.ApiImplicitParams;
import io.swagger.annotations.ApiOperation;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.BeanUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.cache.annotation.CacheEvict;
import org.springframework.cache.annotation.Cacheable;
import org.springframework.web.bind.annotation.*;

import java.util.List;
import java.util.stream.Collectors;

/**
 * 套餐控制层
 */
@Slf4j
@RestController
@Api(tags = "套餐相关类")
@RequestMapping("/setmeal")
public class SetmealController {

    @Autowired
    private CategoryService categoryService;

    @Autowired
    private SetmealService setmealService;

    /**
     * 新增
     * CacheEvict注解清除在Redis中存储数据，allEntries表示是否需要清除缓存中的所有元素
     *
     * @param setmealDto 前端传过来的数据
     * @return 返回是否新增成功
     */
    @PostMapping
    @ApiOperation(value = "新增套餐接口")
    @CacheEvict(value = "setmealCache", allEntries = true)
    public R<String> save(@RequestBody SetmealDto setmealDto) {
        setmealService.saveWithDish(setmealDto);
        return R.success("新增成功");
    }

    /**
     * @param page     页数
     * @param pageSize 每页数量
     * @param name     查询套餐名称
     * @return R<Page < SetmealDto>>
     */
    @GetMapping("/page")
    @ApiImplicitParams({
            @ApiImplicitParam(name = "page", value = "页码", required = true),
            @ApiImplicitParam(name = "pageSize", value = "每页记录数量", required = true),
            @ApiImplicitParam(name = "name", value = "需要模糊查询的套餐名称", required = false)
    })
    public R<Page<SetmealDto>> page(int page, int pageSize, String name) {
        // 构造查询setmeal表的分页构造器
        Page<Setmeal> setmealPage = new Page<>(page, pageSize);
        // 构造返回结果的分页构造器，将setmeal表的分页构造器赋值给该构造器并查询套餐分类名称
        Page<SetmealDto> setmealDtoPage = new Page<>();

        // 构造setmeal表的条件构造器，并添加条件
        LambdaQueryWrapper<Setmeal> wrapper = new LambdaQueryWrapper<>();
        wrapper.like(name != null, Setmeal::getName, name);
        wrapper.orderByDesc(Setmeal::getUpdateTime);

        // 分页查询信息
        setmealService.page(setmealPage, wrapper);

        // 将setmealPage对象信息拷贝至setmealDtoPage里面，忽略records信息。records里面记录着分页查询出来的每条数据信息
        BeanUtils.copyProperties(setmealPage, setmealDtoPage, "records");

        // 将setmealPage里面查询出来的数据赋值到setmealDtoPage里面，并查询出来套餐分类名称
        List<Setmeal> setmeals = setmealPage.getRecords();
        List<SetmealDto> list = setmeals.stream().map((item) -> {
            // 创建setmealDto并将item属性赋值给它
            SetmealDto setmealDto = new SetmealDto();
            BeanUtils.copyProperties(item, setmealDto);

            // 根据分类ID查询分类对象
            Category category = categoryService.getById(item.getCategoryId());

            // 在setmealDto保存查询分类对象的分类名称
            if (category != null) {
                setmealDto.setCategoryName(category.getName());
            }

            // 返回数据为setmealDto，这就是Map流的映射，转换数据类型
            return setmealDto;
        }).collect(Collectors.toList());
        setmealDtoPage.setRecords(list);

        return R.success(setmealDtoPage);
    }

    /**
     * 根据ID删除要进行判断：
     * 1.全部起售状态，错误提示 选中的套餐均为启售状态，不能删除
     * 2.部分起售状态，成功提示 部分套餐为起售状态，不能删除，其余已删除
     * 3.没有起售状态，成功提示 删除套餐成功
     * CacheEvict注解清除在Redis中存储数据，allEntries表示是否需要清除缓存中的所有元素
     *
     * @param ids 需要删除套餐ID数组
     * @return 删除结果
     */
    @DeleteMapping
    @CacheEvict(value = "setmealCache", allEntries = true)
    public R<String> delete(String[] ids) {
        int index = 0;
        // 遍历id
        for (String id : ids) {
            Setmeal setmeal = setmealService.getById(id);
            // 处于停售状态
            if (setmeal.getStatus() != 1) {
                // 删除套餐信息，套餐关联菜品信息，套餐图片
                setmealService.removeWithDishImgById(id);
            } else {
                index++;
            }
        }

        // 如果全部为起售状态
        if (index > 0 && index == ids.length) {
            return R.error("选中的套餐均为启售状态，不能删除");
        } else if (index > 0) {
            return R.success("部分套餐为起售状态，不能删除，其余已删除");
        } else {
            return R.success("删除成功");
        }
    }


    /**
     * 套餐修改状态
     * CacheEvict注解清除在Redis中存储数据，allEntries表示是否需要清除缓存中的所有元素
     *
     * @param status 前端传过来需要修改为的状态，0停售，1起售
     * @param ids    需要修改状态的id数组
     * @return 返回是否修改成功
     */
    @PostMapping("/status/{status}")
    @CacheEvict(value = "setmealCache", allEntries = true)
    public R<String> sale(@PathVariable int status, String[] ids) {
        for (String id : ids) {
            // 获取套餐信息
            Setmeal setmeal = setmealService.getById(id);
            // 修改套餐状态
            setmeal.setStatus(status);
            // 更新套餐信息
            setmealService.updateById(setmeal);
        }
        return R.success("修改成功");
    }

    /**
     * 前端修改套餐信息的时候需要进行回显，所以需要根据套餐ID进行查询数据然后回显
     *
     * @param id 套餐ID
     * @return 查询多表数据，以Dto方式回显至前端
     */
    @GetMapping("/{id}")
    public R<SetmealDto> getById(@PathVariable String id) {
        SetmealDto setmealDto = setmealService.getByIdWithDish(id);
        return R.success(setmealDto);
    }

    /**
     * 修改套餐信息对应的操作
     * CacheEvict注解清除在Redis中存储数据，allEntries表示是否需要清除缓存中的所有元素
     *
     * @param setmealDto 需要修改的套餐信息
     * @return 返回是否修改成功
     */
    @PutMapping
    @CacheEvict(value = "setmealCache", allEntries = true)
    public R<String> update(@RequestBody SetmealDto setmealDto) {
        // 修改套餐信息需要修改两张表：套餐表以及套餐与菜品关系表
        setmealService.updateWithDish(setmealDto);
        return null;
    }

    /**
     * 查询套餐分类ID下面的套餐列表
     * Cacheable注解存储返回值，R要实现序列化接口，只有返回值不为空才会存储
     *
     * @param setmeal 存储前端传过来的套餐分类ID以及套餐状态
     * @return 返回查询出来的套餐列表
     */
    @GetMapping("/list")
    @Cacheable(value = "setmealCache", key = "#setmeal.categoryId+'_'+#setmeal.status", unless = "#result == null")
    public R<List<Setmeal>> getSetmealList(Setmeal setmeal) {
        // 构造条件查询
        LambdaQueryWrapper<Setmeal> lambdaQueryWrapper = new LambdaQueryWrapper<>();
        // 添加条件
        lambdaQueryWrapper.eq(setmeal.getCategoryId() != null, Setmeal::getCategoryId, setmeal.getCategoryId());
        lambdaQueryWrapper.eq(setmeal.getStatus() != null, Setmeal::getStatus, setmeal.getStatus());
        lambdaQueryWrapper.orderByDesc(Setmeal::getUpdateTime);

        // 查询
        List<Setmeal> list = setmealService.list(lambdaQueryWrapper);
        if (list != null) {
            return R.success(list);
        }
        return R.error("查询失败");
    }


    /**
     * 查询该套餐关联的所有菜品详细信息，
     * 例如根据儿童套餐B计划ID1649293469551718402查询setmeal_dish表查询关联的菜品信息
     *
     * @param id 套餐ID
     * @return 返回该套餐关联的菜品详细信息
     */
    @GetMapping("/dish/{id}")
    public R<List<DishDto>> getMealDishDetails(@PathVariable String id) {
        // 首先判断该ID是否是套餐ID，如果是套餐ID那么就查询，如果不是则返回错误信息
        if (setmealService.getById(id) == null) {
            return R.error("该菜品不是套餐，所以无法查询详细信息！");
        }

        List<DishDto> dishDtoList = setmealService.getDishDtoBySetmealId(id);
        if (dishDtoList != null) {
            return R.success(dishDtoList);
        }
        return R.error("查询失败！");
    }
}
